﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace DotNetDI
{
    public static class AppBuilderExtensions
    {
        public static ServiceProvider UserAppDowork<TAppDoWork>(this ServiceProvider serviceProvider, string[] args) where TAppDoWork : class
        {
            try
            {
                using (IServiceScope serviceScope = serviceProvider.CreateScope())
                {
                    ConstructorInfo constructorInfo = typeof(TAppDoWork).GetConstructors().Last();
                    var constructorParams = constructorInfo.GetParameters()
                            .Select(parameterInfo => serviceScope.ServiceProvider.GetRequiredService(parameterInfo.ParameterType))
                            .ToArray();
                    TAppDoWork appDoWork = Activator.CreateInstance(typeof(TAppDoWork), constructorParams) as TAppDoWork;
                    MethodInfo methodInfoStart = appDoWork.GetType().GetMethod("Before");
                    methodInfoStart?.Invoke(appDoWork, GetMethodParame(args, constructorInfo, methodInfoStart));
                    MethodInfo methodInfoRun = appDoWork.GetType().GetMethod("Run");
                    methodInfoRun?.Invoke(appDoWork, GetMethodParame(args, constructorInfo, methodInfoRun));
                    MethodInfo methodInfoEnd = appDoWork.GetType().GetMethod("After");
                    methodInfoEnd?.Invoke(appDoWork, GetMethodParame(args, constructorInfo, methodInfoEnd));
                }
                return serviceProvider;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }

        private static object[] GetMethodParame(string[] args, ConstructorInfo constructorInfo, MethodInfo methodInfoStart)
        {
            object[] objects;
            var methodParams = methodInfoStart.GetParameters();
            switch (methodParams.Length)
            {
                case 0: objects = new object[] { }; break;
                case 1:
                    if (methodParams[0].Name == "args")
                        objects = new object[] { args };
                    else
                        throw new Exception($"Method {methodInfoStart.Name} in class {constructorInfo.DeclaringType.Name} can only have parameter 'string [] args'");
                    break;
                default:
                    throw new Exception($"Method {methodInfoStart.Name} in class {constructorInfo.DeclaringType.Name} can only have parameter 'string [] args'");
            }
            return objects;
        }
    }
}
